<!--
 * @title: 附件组件（带按钮打开对话框形式）
 * @params1: sourceType: 附件sourceType
 * @params2: sourceId: 附件sourceId
 * @params3: limitCount: 限制可上传最大文件数，不限制时不用设置
 * @params4: limitType: 附件类型 jpg;gif;png形式
 * @params5: limitSize: 单个附件大小 单位Kb
 * @params6: extendData: 上传和查询时，传递的附加参数
 * @params7: editable: 是否对附件可操作
 * @params7: previewMode：在此模式下，若附件的editable为false，则直接打开预览模式
 * @params8: extendDesc: 对附件上传的附加说明
 * @params9: title: 附件对话框名称
 * @params10: disabled: 禁用附件按钮
 * @params11: btnTitle: 按钮名称
 * @params12: defaultListType: 默认显示方式
 * @method1: close: 关闭时触发函数，会传递当前的附件列表
 * @description:
 * @fileName: component.vue
 * @author: zhanglongfei
 * @date: 2020-02-17 14:32:02
!-->
<template>
  <div class="tl-upload-container">
    <slot>
      <el-button icon="el-icon-paperclip" :type="btnType" :disabled="disabled" @click="openUploadDialog()" v-if="isDisplayDefaultElement">{{ btnTitle }}[{{ fileList.length > 0 ? fileList.length : fileCount }}]</el-button>
    </slot>
    <el-dialog v-if="!(previewMode && !editable)" :visible.sync="visible" :title="title" :close-on-click-modal="false" v-dialogDrag class="tl-dialog" @open="open" @close="close" width="1200px" :append-to-body="true" :modal-append-to-body="true">
      <el-row>
        <el-col :span="16">
          <el-upload class="tl-upload" v-if="editable" :show-file-list="false" :action="baseUrl + url.upload" :headers="header" :file-list="uploadFileList" :data="postData" :on-success="uploadSuccess" :before-upload="beforeUpload" :on-error="uploadFail" :on-progress="progress">
            <el-button slot="default" icon="el-icon-upload" type="primary" plain :disabled="!editable" style="display:block">添加文件</el-button>
            <div class="upload-queue">
              <el-tooltip class="item" :key="file.uid" v-for="file in uploadingFileList" effect="dark" :content="file.name" placement="top">
                <el-progress :text-inside="true" :stroke-width="20" :percentage="parseInt(file.percentage)" :format="formatProcess"></el-progress>
              </el-tooltip>
            </div>
            <div slot="tip" class="el-upload__tip">
              <span v-if="limitType"> 请上传{{ limitType }}文件&nbsp;&nbsp;</span>
              <span v-if="limitSize">单个附件不超过{{ limitSize }}kb&nbsp;&nbsp;</span>
              <span v-if="extendDesc">{{ extendDesc }}kb&nbsp;&nbsp;</span>
            </div>
          </el-upload>
          <span v-else>&nbsp;</span>
        </el-col>
        <el-col :span="8" style="text-align:right;padding-bottom:5px;">
          <el-radio-group v-model="listType" style="float:right" class="exchange-tab">
            <el-radio-button label="table"><i class="el-icon-date"/></el-radio-button>
            <el-radio-button v-if="editable" label="thumb"><i class="el-icon-picture-outline"/></el-radio-button>
            <el-radio-button v-if="editable" label="list"><i class="el-icon-s-operation"/></el-radio-button>
          </el-radio-group>
          <el-button icon="el-icon-data-board" type="primary" v-if="editable" @click="previewVisible = true" style="float:right;margin-right:5px;">预览模式</el-button>
        </el-col>
      </el-row>
      <div class="tl-upload">
        <ul class="el-upload-list el-upload-list--picture-card" v-if="listType === 'thumb'">
          <li :tabindex="index" class="el-upload-list__item is-success" v-for="(file, index) in fileList" :key="file.id">
            <div>
              <el-avatar shape="square" :size="136" fit="fit" :src="file.ex_base64 ? file.ex_base64 : file.ex_typeUrl"></el-avatar>
              <p class="name">{{ file.displayname }}</p>
              <span class="el-upload-list__item-actions">
                <span class="el-upload-list__item-preview" v-if="file.ex_type === 'image' || file.ex_type === 'pdf'"><i class="el-icon-zoom-in" @click.stop="handleView(file)"></i></span>
                <span v-if="editable" class="el-upload-list__item-delete" @click.stop="handleDownload(file)"><i class="el-icon-download"></i></span>
                <span v-if="editable" class="el-upload-list__item-delete" @click.stop="handleRemove(file)"><i class="el-icon-delete"></i></span>
              </span>
            </div>
          </li>
        </ul>
        <ul class="el-upload-list el-upload-list--text" v-if="listType === 'list'">
          <li :tabindex="index" class="el-upload-list__item is-success" v-for="(file, index) in fileList" :key="file.id">
            <a class="el-upload-list__item-name"><i class="el-icon-document"></i>{{ file.displayname }}</a>
            <label class="el-upload-list__item-status-label">
              <i class="el-icon-zoom-in" v-if="file.ex_type === 'image' || file.ex_type === 'pdf'" @click.stop="handleView(file)"></i>
              <i v-if="editable" class="el-icon-download" @click.stop="handleDownload(file)"></i>
              <i v-if="editable" class="el-icon-delete" @click.stop="handleRemove(file)"></i>
            </label>
          </li>
        </ul>
        <el-table v-if="listType === 'table'" ref="mainTable" class="tl-table" height="300px" :data="fileList" style="width: 100%;" row-key="id" border fit :default-sort="{ prop: 'id', order: 'ascending' }">
          <el-table-column type="index" label="序号" width="55px" align="center"> </el-table-column>
          <el-table-column prop="displayname" label="文件名称" :show-overflow-tooltip="true"></el-table-column>
          <el-table-column prop="filetype" label="文件类型" width="120px" align="center"></el-table-column>
          <el-table-column prop="filesize" label="文件大小" width="120px" align="center"></el-table-column>
          <el-table-column prop="createon" label="上传时间" width="180px" :formatter="dateFormat"></el-table-column>
          <el-table-column label="操作" width="210px" header-align="center" align="center" v-slot="slotProps" fixed="right">
            <el-button type="text" icon="el-icon-view" @click="handleView(slotProps.row)">查看</el-button>
            <el-button type="text" icon="el-icon-download" @click="handleDownload(slotProps.row)">下载</el-button>
            <el-button type="text" icon="el-icon-delete" @click="handleRemove(slotProps.row)" :disabled="!editable">删除</el-button>
          </el-table-column>
        </el-table>
        <div :id="viewerId"></div>
        <el-dialog :visible.sync="pdfVisible" :title="pdfTitle" v-dialogDrag class="tl-dialog" width="90%" :append-to-body="true">
          <iframe v-show="pdfFile" :src="pdfFile" width="100%" height="600px"></iframe>
          <div slot="footer" class="dialog-footer" style="text-align:right;">
            <el-button type="primary" @click="pdfVisible = false">关闭</el-button>
          </div>
        </el-dialog>
      </div>
      <div slot="footer" class="dialog-footer" style="text-align:right;">
        <el-button type="primary" @click="visible = false">关闭</el-button>
      </div>
    </el-dialog>
    <el-dialog :visible.sync="previewVisible" :title="previewTitle" :close-on-click-modal="false" top="10px" width="96%" v-dialogDrag class="tl-dialog" @open="previewOpen" @close="previewClose" :append-to-body="true" :modal-append-to-body="true">
      <tl-preview ref="preview" :sourceType="sourceType" :sourceId="sourceId" :extendData="extendData" :outFileList="fileList"></tl-preview>
      <div slot="footer" class="dialog-footer" style="text-align:right;">
        <el-button type="primary" @click="previewVisible = false">关闭</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import axios from '@tapui/utils/request'
import word from '@tapui/imgs/file/word.png'
import excel from '@tapui/imgs/file/excel.png'
import pdf from '@tapui/imgs/file/pdf.png'
import zip from '@tapui/imgs/file/zip.png'
import image from '@tapui/imgs/file/image.png'
import txt from '@tapui/imgs/file/txt.png'
import ppt from '@tapui/imgs/file/ppt.png'
import video from '@tapui/imgs/file/video.png'
import other from '@tapui/imgs/file/other.png'
import { getAction, deleteAction, putAction } from '@tapui/utils/http'
import Viewer from 'viewerjs'
import 'viewerjs/dist/viewer.min.css'
import tlPreview from '@tapui/views/cbo/cboAttachment/preview'
export default {
  name: 'appointmentAttachment',
  components: { tlPreview },
  data() {
    return {
      fileList: [],
      uploadFileList: [],
      fileCount: 0,
      waitCount: 0,
      previewTitle: '附件预览',
      previewVisible: false,
      postData: {
        sourceType: this.sourceType,
        sourceId: this.sourceId
      },
      baseUrl: axios.defaults.baseURL,
      header: {
        Authorization: 'Bearer ' + this.$store.getters.token
      },
      imageViewer: null,
      totalPage: 0,
      visible: false,
      pdfVisible: false,
      pdfTitle: 'pdf文件',
      pdfFile: null,
      url: {
        upload: '/sd/projectAppointment/upload',
        download: '/cbo/attachment/download',
        display: '/cbo/attachment/display',
        query: '/cbo/attachment/queryAttachmentList',
        delete: '/cbo/attachment/delete',
        queryCount: '/cbo/attachment/calculateCount',
        viewWord: '/sd/projectAppointment/download'
      },
      fileTypeIcon: {
        ';pdf;': pdf,
        ';doc;docx;': word,
        ';xls;xlsx;': excel,
        ';zip;rar;7z;': zip,
        ';jpg;gif;png;bmp;': image,
        ';txt;log;': txt,
        ';ppt;pptx;': ppt,
        ';mp3;mp4;mkv;rmvb': video
      },
      listType: 'table',
      viewerId: 'viewer'
    }
  },
  props: {
    sourceType: {
      type: String,
      required: true
    },
    defaultListType: {
      type: String,
      default: 'table'
    },
    sourceId: {
      type: Number,
      default: 0
    },
    limitCount: {
      type: Number,
      default: 99999
    },
    limitType: {
      type: String,
      default: 'docx,doc,pdf'
    },
    limitSize: {
      // 单位Kb
      type: Number,
      default: 0
    },
    extendData: {
      type: Object,
      default: function() {
        return {}
      }
    },
    editable: {
      type: Boolean,
      default: true
    },
    previewMode: {
      type: Boolean,
      default: true
    },
    extendDesc: {
      type: String,
      default: ''
    },
    title: {
      type: String,
      default: '附件'
    },
    btnTitle: {
      type: String,
      default: '附件'
    },
    disabled: {
      type: Boolean,
      default: false
    },
    isDisplayDefaultElement: {
      default: true,
      type: Boolean
    },
    btnType: {
      type: String,
      default: 'text'
    }
  },
  computed: {
    imageList() {
      return this.fileList.filter((file) => file.ex_type === 'image')
    },
    // 正在上传的文件列表，用于显示进度条
    uploadingFileList() {
      return this.uploadFileList.filter((file) => !file.success)
    }
  },
  methods: {
    openUploadDialog() {
      if (!(this.previewMode && !this.editable)) {
        this.visible = true
      } else {
        this.previewVisible = true
      }
    },
    open() {
      this.postData = {
        sourceType: this.sourceType,
        sourceId: this.sourceId
      }
      this.viewerId = 'viewer' + this.sourceType + this.sourceId + Math.floor(Math.random() * 100)
      this.postData = { ...this.postData, ...this.extendData }
      this.loadData()
      this.listType = this.defaultListType
    },
    previewOpen() {
      this.$nextTick((_) => {
        this.$refs.preview.open()
      })
    },
    previewClose() {
      this.$nextTick((_) => {
        this.$refs.preview.close()
      })
    },
    loadData() {
      let data = {
        qry_sourceType: this.sourceType,
        qry_sourceId: this.sourceId
      }
      this.fileList = []
      getAction(this.url.query, { ...data, ...this.extendData }).then((res) => {
        if (res.success) {
          res.result.forEach((file) => {
            this.bindFileDisplayUrl(file)
          })
          this.fileList = res.result
        } else {
          this.$tap.alert(res.message, 'warning')
        }
      })
    },
    /** 格式化日期 YYYY-MM-dd HH:mm:ss */
    dateFormat(row, column, cellValue, index) {
      let dateValue = row[column.property]
      return this.$tap.date.parseTime(dateValue)
    },
    beforeUpload(file) {
      if (this.limitCount < this.fileList.length + 1 + this.waitCount) {
        this.$tap.alert(`最多只能上传${this.limitCount}个附件!`, 'warning')
        return false
      }
      if (this.limitType) {
        let fileType = this.getFileExt(file.name)
        let isRight = false
        for (let type of this.limitType.split(';')) {
          if (type.indexOf(fileType) > -1) {
            isRight = true
            break
          }
        }
        if (!isRight) {
          this.$tap.alert(`上传附件只能是 ${this.limitType}格式!`, 'warning')
          return false
        }
      }
      if (this.limitSize > 0) {
        if (file.size / 1024 > this.limitSize) {
          this.$tap.alert(`上传附件不能大于 ${this.limitSize}kb!`, 'warning')
          return false
        }
      }
      this.waitCount++
      return true
    },
    /** zhanglongfei 2020.9.18增加上传文件进度条，配套有compute中的uploadingFileList，data中uploadFileList,上传组件的file-list和class=upload-queue的div */
    progress(event, file, fileList) {
      this.uploadFileList = fileList
    },
    formatProcess(percentage) {
      return `${percentage}%`
    },
    uploadSuccess(response, file) {
      this.waitCount--
      let existFile = this.uploadFileList.find((m) => m.uid === file.uid)
      if (existFile) {
        this.$set(existFile, 'success', true)
      }
      if (response.success) {
        this.$tap.message('上传成功')
        this.bindFileDisplayUrl(response.result)
        this.fileList = []
        this.fileList.push(response.result)
      } else {
        this.$tap.alert(`上传失败：${response.message}`, 'warning')
      }
    },
    uploadFail() {
      this.waitCount--
    },
    bindFileDisplayUrl(file) {
      file.ex_typeUrl = other
      file.ex_type = 'other'
      file.ex_base64 = ''
      let fileType = ';' + this.getFileExt(file.displayname) + ';'
      for (let key in this.fileTypeIcon) {
        if (key.indexOf(fileType) > -1) {
          file.ex_typeUrl = this.fileTypeIcon[key]
          // 目前在线显示的文件，仅支持图片和pdf
          if (key.indexOf(';jpg;') > -1) {
            file.ex_type = 'image'
            // if (this.listType === 'thumb') this.display(file)
            this.display(file)
          } else if (key.indexOf(';pdf;') > -1) {
            file.ex_type = 'pdf'
          }
          break
        }
      }
    },
    display(file) {
      if (!file.id) return
      axios({
        url: this.url.download,
        method: 'post',
        data: { attachmentId: file.id },
        responseType: 'arraybuffer'
      }).then((response) => {
        file.ex_base64 = 'data:image/png;base64,' + this.$tap.util.bufferToBase64(response)
      })
    },
    async asyncDisplay(file) {
      if (!file.id) return
      let res = await axios({
        url: this.url.download,
        method: 'post',
        data: { attachmentId: file.id },
        responseType: 'arraybuffer'
      })
      file.ex_base64 = 'data:image/png;base64,' + this.$tap.util.bufferToBase64(res)
    },
    getFileExt(fileName) {
      let arr = fileName.split('.')
      if (arr.length === 0) return ''
      else return arr[arr.length - 1].toString().toLowerCase()
    },
    handleView(file) {
      if (file.ex_type === 'image') {
        this.viewImage(file)
      } else if (file.ex_type === 'pdf') {
        this.viewPdf(file)
      } else {
        this.viewWord(file)
      }
    },
    viewPdf(file) {
      const loading = this.$loading({
        lock: true,
        text: 'Loading',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.7)'
      })
      axios({
        url: this.url.download,
        method: 'post',
        data: { attachmentId: file.id },
        responseType: 'blob'
      }).then((response) => {
        loading.close()
        let url = this.getObjectURL(response)
        this.pdfFile = `/static/plugins/pdfjs/web/viewer.html?file=${url}`
        this.pdfTitle = file.displayname
        this.pdfVisible = true
      })
    },
    viewImage(file) {
      // for (let item of this.imageList) {
      //   if (!item.ex_base64) this.asyncDisplay(item)
      // }
      if (!this.imageViewer) {
        this.imageViewer = new Viewer(document.getElementById(this.viewerId), {
          url(image) {
            return image.ex_base64
          },
          title: [
            1,
            (image, imageData) => {
              return `${this.imageList[this.imageViewer.index].displayname} (${imageData.naturalWidth} × ${imageData.naturalHeight})`
            }
          ]
        })
      }
      this.imageViewer.options.zIndex = 666666
      this.imageViewer.images = this.imageList
      this.imageViewer.length = this.imageViewer.images.length
      let idx = this.imageList.findIndex((item) => item.id === file.id)
      this.imageViewer.index = idx
      this.imageViewer.show()
    },
    viewWord(file) {
      const loading = this.$loading({
        lock: true,
        text: 'Loading',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.7)'
      })
      axios({
        url: this.url.viewWord,
        method: 'post',
        data: { attachmentId: file.id },
        responseType: 'blob'
      }).then((response) => {
        loading.close()
        let url = this.getObjectURL(response)
        this.pdfFile = `/static/plugins/pdfjs/web/viewer.html?file=${url}`
        this.pdfTitle = file.displayname
        this.pdfVisible = true
      })
    },
    getObjectURL(file) {
      let url = null
      if (window.createObjectURL !== undefined) {
        // basic
        url = window.createObjectURL(file)
      } else if (window.webkitURL !== undefined) {
        // webkit or chrome
        try {
          url = window.webkitURL.createObjectURL(file)
        } catch (error) {}
      } else if (window.URL !== undefined) {
        // mozilla(firefox)
        try {
          url = window.URL.createObjectURL(file)
        } catch (error) {}
      }
      return url
    },
    handleRemove(file, fileList) {
      this.$tap.confirm('确定移除文件').then(() => {
        this.doRemove(file)
      })
    },
    doRemove(file) {
      deleteAction(this.url.delete, { ids: file.id }).then((res) => {
        if (res.success) {
          this.$tap.message('删除成功')
          this.fileList.splice(this.fileList.findIndex((item) => item.id === file.id), 1)
          this.fileCount = this.fileList.length
        } else {
          this.$tap.alert(`删除失败：${res.message}`, 'warning')
        }
      })
    },
    handleDownload(file) {
      axios({
        url: this.url.download,
        method: 'post',
        data: { attachmentId: file.id },
        responseType: 'blob'
      }).then((response) => {
        this.doDownload(response, file)
      })
    },
    doDownload(response, file) {
      var blob = new Blob([response], { type: 'application/octet-stream;charset=utf-8' })
      var tempObjectUrl = window.URL.createObjectURL(blob)
      var aLink = document.createElement('a')
      aLink.style.display = 'none'
      aLink.download = file.displayname
      aLink.href = tempObjectUrl
      document.body.appendChild(aLink)
      aLink.click()
      document.body.removeChild(aLink)
      window.URL.revokeObjectURL(tempObjectUrl)
    },
    handleExceed(files, fileList) {
      this.$tap.alert(`最多可以上传${this.limitCount}个文件`, 'warning')
    },
    queryCount() {
      if (!this.sourceType || !this.sourceId) {
        this.fileList = []
        this.fileCount = 0
        return
      }
      this.postData = {
        sourceType: this.sourceType,
        sourceId: this.sourceId
      }
      getAction(this.url.queryCount, this.postData).then((res) => {
        this.fileList = []
        this.fileCount = res.result
      })
    },
    close() {
      this.waitCount = 0
      this.uploadFileList = []
      this.$emit('close', this.fileList)
    }
  },
  mounted() {
    this.queryCount()
  },
  watch: {
    sourceType(newVal, oldVal) {
      this.queryCount()
    },
    sourceId(newVal, oldVal) {
      this.queryCount()
    }
  }
}
</script>

<style lang="scss">
.exchange-tab .el-radio-button span.el-radio-button__inner {
  padding-right: 10px;
  padding-left: 10px;
}
</style>
<style scoped lang="scss">
.tl-upload {
  position: relative;
}
.upload-queue {
  clear: both;
  position: absolute;
  width: 80%;
  background: white;
  text-align: left;
  z-index: 2;
  left: 125px;
  top: 0;
  .el-progress {
    display: inline-block;
    margin: 6px 10px 0 0;
    width: 150px;
  }
}
</style>
